﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace SKDB_Matcher
{

    class CMatcher
    {     
        static List<CAbstractAlgorithm> m_Algorithms;
        static CMatchMatrix m_Matrix;

        static CMatcher s_Singleton;

        private DataSet m_Source;
        private DataSet m_Target;

        private List<DataColumn> m_SourceColumns;
        private List<DataColumn> m_TargetColumns;

        private Boolean m_SuccesfullMatching;

        private CMatcher()
        {
            m_Algorithms = new List<CAbstractAlgorithm>();
            m_Algorithms.Add(new CAlgorithm_DataType());
            m_Algorithms.Add(new CAlgorithm_Content());
            m_Algorithms.Add(new CAlgorithm_Dice());

            m_SuccesfullMatching = false;
        }

        public static void CreateInstance()
        {
            if (s_Singleton == null)
                s_Singleton = new CMatcher();
        }

        public void Reset(DataSet Source, DataSet Target)
        {
            m_Source = Source;
            m_Target = Target;

            m_SourceColumns = new List<DataColumn>();
            m_TargetColumns = new List<DataColumn>();

            foreach (DataTable Table in m_Source.Tables)
            {
                foreach (DataColumn Column in Table.Columns)
                {
                    m_SourceColumns.Add(Column);
                }
            }

            foreach (DataTable Table in m_Target.Tables)
            {
                foreach (DataColumn Column in Table.Columns)
                {
                    m_TargetColumns.Add(Column);
                }
            }
            CAbstractAlgorithm.SourceColumCount = m_SourceColumns.Count;
            CAbstractAlgorithm.TargetColumCount = m_TargetColumns.Count;

            m_Matrix = new CMatchMatrix(m_SourceColumns.Count, m_TargetColumns.Count);

            m_SuccesfullMatching = false;
        }

        public static CMatcher GetInstance()
        {
            return s_Singleton;            
        }

        public DataColumn GetBestMatch(DataColumn _Column, ref float Matching)
        {     
            int index = m_TargetColumns.IndexOf(_Column);
            int sourceId = m_Matrix.GetHighestSourceMatch(index, ref Matching);
            if (sourceId >= 0)
                return m_SourceColumns[sourceId];
            else
                return null;
        }

        public DataColumn[] GetAllMatches(DataColumn _Column, ref float[] Matchings)
        {
            
            int index = m_TargetColumns.IndexOf(_Column);
            int[] sourceIds = m_Matrix.GetAllSourceMatches(index, ref Matchings);

            DataColumn[] Result = new DataColumn[sourceIds.Length];
            for (int i = 0; i < sourceIds.Length; i++)
                Result[i] = m_SourceColumns[sourceIds[i]];

            return Result;
        }

        public void Match()
        {
            try
            {
                CConsole.WriteLine("Matching started!");
                float[,] MatchValues;
                m_Matrix.Clear();
                foreach (CAbstractAlgorithm Algo in m_Algorithms)
                {
                    CConsole.WriteLine("Algorithm " + Algo.Caption + " started");
                    MatchValues = Algo.CalcMatches(m_Source, m_Target);
                    for (int i = 0; i < m_SourceColumns.Count; i++)
                        for (int j = 0; j < m_TargetColumns.Count; j++)
                        {
                            if (MatchValues[i, j] > 1.0f)
                            {
                                CConsole.WriteLine("Das sollte niemals passieren: " + Algo.Caption + " - " + MatchValues[i, j].ToString() + " - " + m_SourceColumns[i].ColumnName + " - " + m_TargetColumns[j].ColumnName);
                            }
                            m_Matrix[i, j] += MatchValues[i, j] * Algo.Weight;
                        }
                    CConsole.WriteLine("Algorithm " + Algo.Caption + " finished");
                }
                m_SuccesfullMatching = true;
                CConsole.WriteLine("Matching finished!");
            }
            catch (Exception e)
            {
            }            
        }

        public static List<CAbstractAlgorithm> Algorithms
        {
            get
            {
                return m_Algorithms;                     
            }
        }

        public Boolean SuccesfullMatching
        {
            get { return m_SuccesfullMatching; }
        }
    }

    class CMatchMatrix
    {
        const float MIN_MATCHING = 0.25f;

        private float[,] m_Values;
        private int[] m_Dimensions;

        public CMatchMatrix(int n, int m)
        {
            m_Values = new float[n, m];
            m_Dimensions = new int[2] { n, m };
        }

        public int GetHighestSourceMatch(int m, ref float Matching)
        {
            float Temp = m_Values[0, m];
            int Result = 0;
            for (int i = 0; i < m_Dimensions[0]; i++)
            {
                if (m_Values[i, m] > Temp)
                {
                    Result = i;
                    Temp = m_Values[i, m];
                }
            }
            if (Temp >= MIN_MATCHING)
            {
                Matching = Temp;
                return Result;
            }
            else
            {
                Matching = -1;
                return -1;
            }
        }

        public int[] GetAllSourceMatches(int m, ref float[] Matchings)
        {
            Matchings = new float[m_Dimensions[0]];
            for (int i = 0; i < m_Dimensions[0]; i++)
                Matchings[i] = m_Values[i, m];
            int[] Result = Sort(ref Matchings);
            return Result;
        }

        public float this[int n, int m]
        {
            get { return this.m_Values[n, m]; }
            set { this.m_Values[n, m] = value; }
        }

        public void Clear()
        {
            for (int i = 0; i < m_Dimensions[0]; i++)
                for (int j = 0; j < m_Dimensions[1]; j++)
                    m_Values[i, j] = 0.0f;
        }

        private int[] Sort(ref float[] Array)
        {
            if (Array != null && Array.Length > 0)
            {
                int n = Array.Length;
                int[] indizes = new int[n];
                for (int i = 0; i < n; i++)
                    indizes[i] = i;
                Boolean changed = true;
                while (changed && n > 1)
                {
                    changed = false;
                    for (int i = 0; i < n - 1; i++)
                    {
                        if (Array[i] < Array[i + 1])
                        {
                            float tmp = Array[i];
                            Array[i] = Array[i + 1];
                            Array[i + 1] = tmp;
                            int tmpi = indizes[i];
                            indizes[i] = indizes[i + 1];
                            indizes[i + 1] = tmpi;
                            changed = true;
                        }
                    }
                    n--;
                }

                return indizes;
            }
            else
                return null;

        }

    }
}
